Step 2 - Zoom and rotate the map

Use the pinch manipulator to enable users to zoom and rotate nodes in your Kanzi application. To calculate the amount by which to zoom and rotate a node, the pinch gesture continuously tracks the position and distance between two fingers that move on the device screen.

Use the multi-click manipulator to enable users to multi-click or multi-tap nodes in your Kanzi application. You can set in the multi-click manipulator the number of taps and the maximum amount of time that can lapse between the taps for Kanzi to interpret the taps as a multi-tap gesture.

In this step you first use the pinch manipulator to enable zooming and rotating of the map. Then you use the multi-click manipulator to enable resetting the position, zoom level, and rotation angle of the map.

You deploy the tutorial application to an Android device, because the pinch gesture requires multitouch support.

Zoom and rotate the map

In this section you create and use the pinch manipulator to zoom and rotate the map when the user pinches the map.

To zoom and rotate the map:

  1. In the pan_zoom_tap.cpp file in the private section of the PanZoomTap class define the handlers for the pinch messages:
    private:
    
        ...
    
        // Define the handler for the PinchManipulator::StartedMessage message from the 2D nodes 
        // that have an input manipulator which generates pinch messages.
        // This handler prepares a 2D node for a pinch gesture.
        void onPinchStarted(PinchManipulator::StartedMessageArguments& messageArguments)
        {
            // Get from the message arguments the node that the user pinches.
            Node2DSharedPtr mapNode = dynamic_pointer_cast<Node2D>(messageArguments.getSource());
    
            // Store the initial value of the Render Transformation property Scale property field.
            SRTValue2D nodeTransform = mapNode->getRenderTransformation();
            m_pinchInitialScaleFactor = nodeTransform.getScale().getX() - 1.0f;
    
            // Calculate a minimum scale value based on the size of the application screen.
            ScreenSharedPtr screen = getScreen();
            m_minScale = max(getScreen()->getActualWidth() / mapNode->getActualWidth(), screen->getActualHeight() / mapNode->getActualHeight());
        }
    
        // Define the handler for the PinchManipulator::MovedMessage message from the 2D nodes
        // that have an input manipulator which generates pinch messages.
        // This scales and rotates a 2D node for the amount of the pinch gesture.
        void onPinchMoved(PinchManipulator::MovedMessageArguments& messageArguments)
        {
            // Get from the message arguments the node that the user pinches.
            Node2DSharedPtr mapNode = dynamic_pointer_cast<Node2D>(messageArguments.getSource());
    
            // Get the scale and rotation from the message arguments.
            float scaleDelta = messageArguments.getScale();
            float rotateDelta = messageArguments.getRotation();
    
            // Calculate the scale by adding the initial scale to the pinch value.
            // Restrict the scale so that the map cannot be smaller than the application screen size.
            float scale = max(m_minScale, scaleDelta + m_pinchInitialScaleFactor);
    
            // Get the Render Transformation property of the Map node.
            SRTValue2D mapRenderSRT = mapNode->getRenderTransformation();
    
            // Apply the rotation.
            mapRenderSRT.rotate(rotateDelta);
    
            // Apply the scale.
            mapRenderSRT.setScale(Vector2(scale, scale));
    
            // Get the world transformation of the Map node.
            // You use this in the next step of the tutorial.
            Matrix3x3 mapWorldTransform = mapNode->getWorldTransform();
            SRTValue2D mapWorldSRT = *SRTValue2D::create(mapWorldTransform);
    
            // Apply the new render transformation to the Map node.
            mapNode->setRenderTransformation(mapRenderSRT);
        }
    
         // Define a member variable for the initial scale factor of the pinch gesture.
        float m_pinchInitialScaleFactor;
    
        // Define a member variable for the minimum scale value.
        float m_minScale;
    };
  2. In the beginning of the public section of the PanZoomTap class add the constructor and set the initial scale factor for the pinch gesture:
    public:
    
        PanZoomTap() :
            m_pinchInitialScaleFactor(0.0f)
        {
        }
    
        ...
  3. In the onProjectLoaded() function create a PinchManipulator manipulator and subscribe to its messages at the Map node:
        virtual void onProjectLoaded() KZ_OVERRIDE
        {
            ...
    
            // Create an input manipulator that generates pinch messages.
            PinchManipulatorSharedPtr pinchManipulator = PinchManipulator::create(domain);
    
            // Add the input manipulator to the Map node.
            mapNode->addInputManipulator(pinchManipulator);
    
            // Subscribe to the PinchManipulator::StartedMessage message at the Map node.
            // The PinchManipulator generates this message when the user presses two fingers on the Map node.
            mapNode->addMessageHandler(PinchManipulator::StartedMessage, bind(&PanZoomTap::onPinchStarted, this, placeholders::_1));
    
            // Subscribe to the PinchManipulator::MovedMessage message at the Map node.
            // The PinchManipulator generates this message when the scale or rotation threshold is exceeded
            // and when the tracked touches move between updates.
            mapNode->addMessageHandler(PinchManipulator::MovedMessage, bind(&PanZoomTap::onPinchMoved, this, placeholders::_1));
        }
  4. Deploy the application to an Android device, because the pinch gesture requires multitouch support:
    1. Connect your Android device to your computer.
    2. In the Kanzi Studio main menu select File > Export > Build Android Package.
      Kanzi Studio creates an Android package from your Kanzi Studio project, deploys, and runs it on your Android device.

    On your Android device use the pinch gesture to zoom and rotate the map.
    The map rotates around its center point because the origin of the Map node is set to the center.

Reset the position, zoom level, and rotation

In this section you use the multi-click manipulator to reset the position, zoom level, and rotation angle of the map when the user double-taps the map.

To reset the position, zoom level, and rotation:

  1. In the pan_zoom_tap.cpp file in the private section of the PanZoomTap class define the handler for the multi-click message:
    private:
    
        ...
        // Define the handler for the MultiClickManipulator::MultiClickMessage message from the nodes that have
        // an input manipulator which generates the multi-click message when the user double-taps the node.
        void onNodeDoubleTapped(MultiClickManipulator::MultiClickMessageArguments& messageArguments)
        {
            // Get from the message arguments the node that the user double-taps.
            Node2DSharedPtr mapNode = dynamic_pointer_cast<Node2D>(messageArguments.getSource());
    
            // Remove the Render Transformation property of the node.
            // This way you reset the position, zoom level, and rotation angle of the Map node.
            mapNode->removeLocalValue(Node2D::RenderTransformationProperty);
        }
    
        ...
    };
  2. In the onProjectLoaded() function create and configure a MultiClickManipulator manipulator and subscribe to its message at the Map node:
        virtual void onProjectLoaded() KZ_OVERRIDE
        {
            ...
    
            // Create an input manipulator that generates the multi-click message.
            MultiClickManipulatorSharedPtr multiClickManipulator = MultiClickManipulator::create(domain);
    
            // Add the input manipulator to the Map node.
            mapNode->addInputManipulator(multiClickManipulator);
    
            // By default the input manipulator recognizes two taps as a double-tap gesture.
            // To use a different number of taps, use the MultiClickManipulator::setExpectedClicks function.
            // For example, to set the input manipulator to recognize three taps as a multi-click gesture pass 3 to the function.
            // multiClickManipulator->setExpectedClicks(3);
    
            // Set the timeout of the double-tap to 300 ms. If the time between two taps does not exceed this value,
            // the input manipulator recognizes these taps as a double-tap gesture. The default timeout is 250 ms.
            multiClickManipulator->setTimeout(chrono::milliseconds(300));
    
            // Subscribe to the MultiClickManipulator::MultiClickMessage message at the Map node.
            // The MultiClickManipulator manipulator generates this message when the user double-taps the node.
            mapNode->addMessageHandler(MultiClickManipulator::MultiClickMessage, bind(&PanZoomTap::onNodeDoubleTapped, this, placeholders::_1));
        }
  3. Build and deploy the application to your Android device.
    In the application first use the pinch gesture to zoom and rotate the map, and the pan gesture to move the map. Then double-tap the map to set it to the initial position, zoom level, and rotation angle.

See also

To learn more about the pinch manipulator, see Using the pinch manipulator.

To learn more about the multi-click manipulator, see Using the multi-click manipulator.

To learn more about deploying Kanzi applications to Android, see Deploying Kanzi applications to Android.


< PREVIOUS STEP
NEXT STEP >